#include <stdlib.h>
#include <stdio.h>

#include "aceMesh_runtime_c.h"
#include "fdtd_aceMesh.h"
#include "slave.h"
#include "float.h"
#include "sbmd.h"

extern double ex[NX][NY+1];
extern double ey[NX+1][NY];
extern double hz[NX][NY];

__thread_local_fix double slave_hz_left[TZ1];
__thread_local_fix double slave_hz_up[TZ2];
__thread_local_fix double slave_ex_right[TZ1];
__thread_local_fix double slave_ey_down[TZ2];
__thread_local_fix volatile unsigned long get_reply,put_reply;
//__thread_local_fix double slave[3][TZ1][TZ2];
__thread_local_fix double slave_ex[TZ1][TZ2];
__thread_local_fix double slave_ey[TZ1][TZ2];
__thread_local_fix double slave_hz[TZ1][TZ2];
__thread_local_fix FILE* out;
#ifdef PROFILING
__thread_local_fix int myid=0;
__thread_local_fix volatile unsigned long comp_start,comp_end;
__thread_local_fix volatile unsigned long dma_get_end,dma_get_start,dma_put_end,dma_put_start;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

#define min(a,b) ((a>b)?(b):(a))
#define max(a,b) ((a>b)?(a):(b))

//loop1
     // for (j1=0; j1<NY; j1++)
       // ey1[0][j1] = t1;
void stencil_loop1(targs1* args)
{
    int j;
    int t=args->t;
    int jj=args->jj;
       
    //compute
#ifdef PROFILING
    myid = athread_get_id(-1);
    comp_start=rtc_();
#endif
    for (j=0;j<min(NY,TZ2);j++)
        slave_ey[0][j]=t;
#ifdef PROFILING    
    comp_end=rtc_();  
    dma_put_start=rtc_();
#endif
    //write back
    put_reply=0;
    athread_put(PE_MODE
        , &slave_ey[0][0], &ey[0][jj]
        , TZ2*8, (void*)&put_reply, 0, 0);
    while(put_reply!=1);
#ifdef PROFILING   
    dma_put_end=rtc_();
    slave_comp_count[myid]=slave_comp_count[myid]+(comp_end-comp_start);
    slave_dma_count[myid]=slave_dma_count[myid]+(dma_put_end-dma_put_start);
#endif
}

//loop2
// for (i1=1; i1<NX; i1++)
    // for (j1=0; j1<NY; j1++)
    // {
        // ey1[i1][j1] = ey1[i1][j1] - 0.5*(hz1[i1][j1]-hz1[i1-1][j1]);
    // }
void stencil_loop2(targs2* args)
{
    int ii=args->ii;
    int jj=args->jj;
    int i,j;
    int dma_num=0;
//    int slave_i=0;
    int tsize,bsize,stride;
#ifdef PROFILING    
    myid = athread_get_id(-1);
    dma_get_start=rtc_();
#endif    
    //fetch data
    get_reply=0;
    //get_reply2=0;
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*TZ1;
    athread_get(PE_MODE
        , &ey[ii][jj], &slave_ey[0][0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;   
    tsize= bsize*TZ1;
    athread_get(PE_MODE
        , &hz[ii][jj], &slave_hz[0][0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
    dma_num+=2;
    if (max(ii,1)!=1)
    {
        athread_get(PE_MODE
            , &hz[ii-1][jj], &slave_hz_up[0]
            , TZ2*8, (void*)&get_reply
            , 0, 0, 0);
        dma_num++;
    }
    while (get_reply!=dma_num);
    
    //compute
#ifdef PROFILING
    dma_get_end=rtc_();
    comp_start=rtc_();
#endif
    for(i=1;i<TZ1;i++)
        for(j=0;j<TZ2;j++)
            slave_ey[i][j]=slave_ey[i][j]-0.5*(slave_hz[i][j]-slave_hz[i-1][j]);
//    while(get_reply2!=dma_num);
    if(max(ii,1)!=1)
    {
        for(j=0;j<TZ2;j++)
            slave_ey[0][j]=slave_ey[0][j]-0.5*(slave_hz[0][j]-slave_hz_up[j]);
    }
#ifdef PROFILING    
    comp_end=rtc_();
    dma_put_start=rtc_();
#endif    
    //write back
    put_reply=0;
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;
    tsize= bsize*TZ1;
    athread_put(PE_MODE
        , &slave_ey[0][0], &ey[ii][jj]
        , tsize, (void*)&put_reply
        , stride, bsize);
    while(put_reply!=1);
#ifdef PROFILING    
    dma_put_end=rtc_();
    slave_comp_count[myid]=slave_comp_count[myid]+(comp_end-comp_start);
    slave_dma_count[myid]=slave_dma_count[myid]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);
//    comp_end=comp_start=0;
//    dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
#endif    
}
//loop3
// for (i1=0; i1<NX; i1++)
    // for (j1=1; j1<NY; j1++)
        // ex1[i1][j1] = ex1[i1][j1] - 0.5*(hz1[i1][j1]-hz1[i1][j1-1]);
void stencil_loop3(targs2* args)
{
    int ii=args->ii;
    int jj=args->jj;
    int i,j;
    int tsize,bsize,stride;
    int dma_num = 0;
//    int dma_put_num = 0;
#ifdef PROFILING    
    myid = athread_get_id(-1);
    dma_get_start=rtc_();
#endif 
    //fetch data
    get_reply=0;
    //get_reply2=0;
    bsize=TZ2*8;
    stride=(NY-TZ2+1)*8;
    tsize=bsize*TZ1;
    athread_get(PE_MODE
        , &ex[ii][jj], &slave_ex[0][0]
        , tsize, (void *)&get_reply
        , 0, stride, bsize);
    bsize=TZ2*8;
    stride=(NY-TZ2)*8; 
    tsize= bsize*TZ1;
    athread_get(PE_MODE
        , &hz[ii][jj], &slave_hz[0][0]
        , tsize, (void *)&get_reply
        , 0, stride, bsize);
    dma_num+=2;
    if(max(jj,1)!=1)
    {
        bsize=8;
        stride=(NY-1)*8;
        tsize=bsize*TZ1;
        athread_get(PE_MODE
            , &hz[ii][jj-1], &slave_hz_left[0]
            , tsize, (void *)&get_reply
            , 0, stride,bsize);
        dma_num++;
    }
    while(get_reply!=dma_num);
    
    //compute
#ifdef PROFILING 
    dma_get_end=rtc_();
    comp_start=rtc_();
#endif    
    for(i=0;i<TZ1;i++)
        for(j=1;j<TZ2;j++)
            slave_ex[i][j]=slave_ex[i][j]-0.5*(slave_hz[i][j]-slave_hz[i][j-1]);
//    while(get_reply2!=dma_num);
    if(max(jj,1)!=1)
        for(i=0;i<TZ1;i++)
            slave_ex[i][0]=slave_ex[i][0]-0.5*(slave_hz[i][0]-slave_hz_left[i]);
#ifdef PROFILING    
    comp_end=rtc_();
    dma_put_start=rtc_();
#endif    
    //write back
    put_reply=0;
    // if(max(jj,1)==1)
    // {
        // for(i=0;i<TZ1;i++)
            // athread_put(PE_MODE
            // , &slave[0][i][1], &ex[ii+i][1]
            // , (TZ2-1)*8,(void *)&put_reply
            // , 0, 0);  
        // dma_put_num+=TZ1;
    // }else{
        bsize=(TZ2)*8;
        stride=(NY-TZ2+1)*8; 
        tsize= bsize*TZ1;
        athread_put(PE_MODE
            , &slave_ex[0][0], &ex[ii][jj]
            , tsize, (void *)&put_reply
            , stride, bsize); 
        //dma_put_num++;
    // }
    while(put_reply!=1);
#ifdef PROFILING    
    dma_put_end=rtc_();
    slave_comp_count[myid]=slave_comp_count[myid]+(comp_end-comp_start);
    slave_dma_count[myid]=slave_dma_count[myid]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);
//    comp_end=comp_start=dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
#endif 
}
    
    
//loop4
// for(i1=0; i1<NX; i1++)
    // for(j1=0; j1<NY; j1++)
       // hz1[i1][j1]=hz1[i1][j1]-0.7*(ex1[i1][j1+1]-ex1[i1][j1]+ey1[i1+1][j1]-ey1[i1][j1]);
void stencil_loop4(targs2* args)
{
    int ii=args->ii;
    int jj=args->jj;
    int i,j;
    int bsize,stride,tsize;
//    int dma_num=0;
#ifdef PROFILING    
    myid = athread_get_id(-1);
    dma_get_start=rtc_();
#endif 
    //fetch data
    get_reply=0;
    //get_reply2=0;
    bsize=TZ2*8;
    stride=(NY-TZ2+1)*8;
    tsize=bsize*TZ1;
    athread_get(PE_MODE
        , &ex[ii][jj], &slave_ex[0][0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
//    dma_num++;
    
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&ey[ii][jj]
        , &slave_ey[0][0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
//    dma_num++;
    
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&hz[ii][jj]
        , &slave_hz[0][0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
//    dma_num++;
    
    bsize=8;
    stride=(NY+1-1)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&ex[ii][jj+TZ2]
        , &slave_ex_right[0]
        , tsize, (void*)&get_reply
        , 0, stride, bsize);
//    dma_num++;
    
    athread_get(PE_MODE
        , &ey[ii+TZ1][jj], &slave_ey_down[0]
        , TZ2*8, (void*)&get_reply
        , 0, 0, 0);
//  dma_num++;
    while(get_reply!=5);//
    
    //compute
#ifdef PROFILING    
    comp_start=rtc_();
    dma_get_end=rtc_();
#endif    
    for(i=0; i<TZ1-1; i++)
        for(j=0; j<TZ2-1; j++)
            slave_hz[i][j] = slave_hz[i][j]-\
                0.7*(slave_ex[i][j+1] - slave_ex[i][j]\
                + slave_ey[i+1][j] - slave_ey[i][j]);
//    while(get_reply2!=2);
    for(j = 0; j < TZ2-1; j++)
        slave_hz[TZ1-1][j] = slave_hz[TZ1-1][j] -\
                0.7*(slave_ex[TZ1-1][j+1] - slave_ex[TZ1-1][j]\
                + slave_ey_down[j] - slave_ey[TZ1-1][j]); 
    for(i=0; i<TZ1-1; i++)
        slave_hz[i][TZ2-1] = slave_hz[i][TZ2-1] -\
                0.7*(slave_ex_right[i]- slave_ex[i][TZ2-1]\
                + slave_ey[i+1][TZ2-1] - slave_ey[i][TZ2-1]);
    slave_hz[TZ1-1][TZ2-1] = slave_hz[TZ1-1][TZ2-1]-\
                0.7*(slave_ex_right[TZ1-1] - slave_ex[TZ1-1][TZ2-1]\
                +slave_ey_down[TZ2-1]- slave_ey[TZ1-1][TZ2-1]);
#ifdef PROFILING    
    comp_end=rtc_();
    dma_put_start=rtc_();
#endif    
    //write back
    put_reply=0;
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;
    tsize= bsize*TZ1;
    athread_put(PE_MODE
        , &slave_hz[0][0], &hz[ii][jj]
        , tsize, (void*)&put_reply
        , stride, bsize);
    while(put_reply!=1);
#ifdef PROFILING    
    dma_put_end=rtc_();
    slave_comp_count[myid]=slave_comp_count[myid]+(comp_end-comp_start);
    slave_dma_count[myid]=slave_dma_count[myid]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);
//    comp_end=comp_start=dma_put_end=dma_put_start=dma_get_end=dma_get_start=0;
#endif   
}
